package LRU缓存; // 定义包名

// 导入并发相关的工具类
import java.util.concurrent.ConcurrentHashMap; // 线程安全的HashMap实现
import java.util.concurrent.ConcurrentLinkedQueue; // 线程安全的队列实现
import java.util.concurrent.Executors; // 线程池工厂类
import java.util.concurrent.ScheduledExecutorService; // 定时任务线程池
import java.util.concurrent.TimeUnit; // 时间单位
import java.util.concurrent.locks.Lock; // 锁接口
import java.util.concurrent.locks.ReentrantReadWriteLock; // 可重入读写锁实现

public class ConcurrentLRUwithExpire<K, V> { // 定义带过期时间的线程安全LRU缓存类，支持泛型
    private final int maxCap; // 缓存的最大容量

    private ConcurrentHashMap<K, V> map; // 存储键值对的线程安全Map
    private ConcurrentLinkedQueue<K> keys; // 存储键的线程安全队列，用于维护访问顺序

    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); // 创建读写锁
    private Lock writeLock = readWriteLock.writeLock(); // 获取写锁
    private Lock readLock = readWriteLock.readLock(); // 获取读锁

    private ScheduledExecutorService scheduledExecutorService; // 定时任务线程池，用于处理过期清理

    public ConcurrentLRUwithExpire(int maxCap){ // 构造函数
        if(maxCap < 0){ // 检查容量是否合法
            throw new IllegalArgumentException("Illegal max capacity: " + maxCap);
        }
        this.maxCap = maxCap; // 设置最大容量
        map = new ConcurrentHashMap<>(); // 初始化Map
        keys = new ConcurrentLinkedQueue<>(); // 初始化队列
        scheduledExecutorService = Executors.newScheduledThreadPool(3); // 创建定时任务线程池
    }
    public V put(K key, V value, long expire){ // 添加或更新缓存项，支持过期时间
        writeLock.lock(); // 获取写锁
        try {
            if(map.containsKey(key)){ // 如果键已存在
                moveToTail(key); // 将键移到队列尾部
                map.put(key, value); // 更新值
                return value;
            }
            if(map.size() == maxCap){ // 如果缓存已满
                System.out.println("maxCap of cache reached");
                removeOldest(); // 移除最旧的项
            }
            keys.add(key); // 将新键添加到队列
            map.put(key, value); // 将键值对添加到Map
            if(expire > 0){ // 如果设置了过期时间
                removeAfterExpire(key, expire); // 设置过期清理任务
            }
            return value;
        } finally {
            writeLock.unlock(); // 释放写锁
        }
    }
    public V get(K key){ // 获取缓存项
        readLock.lock(); // 获取读锁
        try {
            if(map.containsKey(key)){ // 如果键存在
                moveToTail(key); // 将键移到队列尾部
                return map.get(key); // 返回值
            }
            return null;
        } finally {
            readLock.unlock(); // 释放读锁
        }
    }
    public V remove(K key){ // 移除缓存项
        writeLock.lock(); // 获取写锁
        try {
            if(map.containsKey(key)){ // 如果键存在
                keys.remove(key); // 从队列中移除键
                return map.remove(key); // 从Map中移除键值对
            }
            return null;
        } finally {
            writeLock.unlock(); // 释放写锁
        }
    }
    private void moveToTail(K key){ // 将键移到队列尾部
        keys.remove(key); // 从队列中移除键
        keys.add(key); // 将键添加到队列尾部
    }
    private void removeOldest(){ // 移除最旧的缓存项
        K oldest = keys.poll(); // 获取并移除队列头部的键
        if(oldest != null){ // 如果队列不为空
            map.remove(oldest); // 从Map中移除对应的键值对
        }
    }
    private void removeAfterExpire(K key, long expire){ // 设置过期清理任务
        scheduledExecutorService.schedule(() -> { // 创建定时任务
            map.remove(key); // 从Map中移除键值对
            keys.remove(key); // 从队列中移除键
        }, expire, TimeUnit.MILLISECONDS); // 设置过期时间
    }
    public int size(){ // 获取当前缓存大小
        return map.size();
    }
    public static void main(String[] args) throws InterruptedException{ // 主方法
        ConcurrentLRUwithExpire<Integer, String> lru = new ConcurrentLRUwithExpire<>(3); // 创建LRU缓存实例
        lru.put(1, "java", 3000); // 添加键值对，3秒后过期
        lru.put(2, "c++", 3000); // 添加键值对，3秒后过期
        lru.put(3, "python", 1500); // 添加键值对，1.5秒后过期
        System.out.println(lru.size()); // 输出当前缓存大小
        Thread.sleep(2000); // 休眠2秒
        System.out.println(lru.size()); // 再次输出缓存大小，此时应该只有2个元素
    }
}
